package com.github.leecho.spring.cloud.gateway.dubbo.starter;

import com.github.leecho.spring.cloud.gateway.dubbo.argument.HttpRequestBodyReader;
import com.github.leecho.spring.cloud.gateway.dubbo.argument.DubboArgumentResolver;
import com.github.leecho.spring.cloud.gateway.dubbo.argument.JacksonHttpRequestBodyReader;
import com.github.leecho.spring.cloud.gateway.dubbo.client.*;
import com.github.leecho.spring.cloud.gateway.dubbo.message.DubboMessageConverter;
import com.github.leecho.spring.cloud.gateway.dubbo.DubboRoutingFilter;
import com.github.leecho.spring.cloud.gateway.dubbo.message.DubboMessageWriter;
import com.github.leecho.spring.cloud.gateway.dubbo.message.JacksonDubboMessageWriter;
import com.github.leecho.spring.cloud.gateway.dubbo.argument.rewirte.DefaultDubboArgumentRewriter;
import com.github.leecho.spring.cloud.gateway.dubbo.argument.rewirte.DubboArgumentRewriter;
import com.github.leecho.spring.cloud.gateway.dubbo.argument.rewirte.variable.loader.*;
import com.github.leecho.spring.cloud.gateway.dubbo.argument.rewirte.variable.render.VariableRender;
import com.github.leecho.spring.cloud.gateway.dubbo.argument.rewirte.variable.render.SpelVariableRender;
import com.github.leecho.spring.cloud.gateway.dubbo.argument.rewirte.variable.render.VelocityVariableRender;
import com.github.leecho.spring.cloud.gateway.dubbo.message.NoOpsDubboMessageConverter;
import com.github.leecho.spring.cloud.gateway.dubbo.route.DefaultDubboRouteFactory;
import com.github.leecho.spring.cloud.gateway.dubbo.route.DubboRouteFactory;
import com.github.leecho.spring.cloud.gateway.dubbo.route.DubboRoutePredicate;
import com.github.leecho.spring.cloud.gateway.dubbo.route.DubboRouteUriSchemePredicate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.gateway.config.conditional.ConditionalOnEnabledGlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.List;

/**
 * @author LIQIU
 * @date 2021/7/2 16:39
 */
@Configuration
@EnableConfigurationProperties(DubboRoutingProperties.class)
public class DubboRoutingAutoConfiguration {


	@Bean
	@ConditionalOnEnabledGlobalFilter
	public DubboRoutingFilter dubboRoutingFilter(DubboRouteFactory routeFactory,
												 DubboRoutePredicate routePredicate,
												 DubboClient dubboClient,
												 DubboMessageConverter converter,
												 DubboArgumentResolver resolver,
												 DubboMessageWriter writer) {
		return new DubboRoutingFilter(routeFactory, routePredicate, dubboClient, writer, resolver, converter);
	}

	@Bean
	@ConditionalOnMissingBean(DubboMessageConverter.class)
	public DubboMessageConverter defaultDubboMessageConverter() {
		return new NoOpsDubboMessageConverter();
	}

	@Bean
	@ConditionalOnMissingBean(DubboGenericServiceFactory.class)
	public DubboGenericServiceFactory dubboGenericServiceFactory(DubboRoutingProperties dubboRoutingProperties) {
		DubboRoutingProperties.Client client = dubboRoutingProperties.getClient();
		DefaultDubboGenericServiceFactory dubboGenericServiceFactory = new DefaultDubboGenericServiceFactory();
		if (client != null) {
			dubboGenericServiceFactory.setInvokeAsync(Boolean.TRUE.equals(client.getInvokeAsync()));
			dubboGenericServiceFactory.setRetries(client.getRetries());
			dubboGenericServiceFactory.setCheck(client.getCheck());
		}
		return dubboGenericServiceFactory;
	}

	@Bean
	@ConditionalOnMissingBean(DubboClient.class)
	public DubboClient dubboClient(@Autowired(required = false) DubboGenericServiceCache genericServiceCache,
								   DubboGenericServiceFactory genericServiceFactory,
								   DubboRoutingProperties dubboRoutingProperties) {
		if (genericServiceCache == null) {
			genericServiceCache = new DefaultDubboGenericServiceCache();
		}
		DubboRoutingProperties.Client client = dubboRoutingProperties.getClient();
		DefaultDubboClient dubboClient = new DefaultDubboClient(genericServiceCache, genericServiceFactory);
		if (client != null) {
			dubboClient.setInvokeAsync(client.getInvokeAsync());
		}
		return dubboClient;
	}

	@Bean
	@ConditionalOnMissingBean(DubboMessageWriter.class)
	public DubboMessageWriter dubboMessageWriter() {
		return new JacksonDubboMessageWriter();
	}

	@Bean
	public DubboArgumentResolver dubboArgumentResolver(DubboArgumentRewriter rewriter,
													   HttpRequestBodyReader httpRequestBodyReader) {
		return new DubboArgumentResolver(rewriter, httpRequestBodyReader);
	}

	@Bean
	@ConditionalOnMissingBean(DubboRouteFactory.class)
	public DubboRouteFactory dubboRouteFactory() {
		return new DefaultDubboRouteFactory();
	}

	@Bean
	@ConditionalOnMissingBean(DubboRoutePredicate.class)
	public DubboRoutePredicate dubboRoutePredicate() {
		return new DubboRouteUriSchemePredicate();
	}

	@Bean
	@ConditionalOnMissingBean(HttpRequestBodyReader.class)
	public HttpRequestBodyReader dubboArgumentReader() {
		return new JacksonHttpRequestBodyReader();
	}

	@Bean
	public DelegateVariableLoader variableProcessor(List<VariableLoader> loaders) {
		if (loaders == null) {
			loaders = new ArrayList<>();
		}
		loaders.add(new BodyVariableLoader());
		loaders.add(new HeaderVariableLoader());
		loaders.add(new QueryVariableLoader());
		loaders.add(new CookieVariableLoader());
		return new DelegateVariableLoader(loaders);
	}

	@Bean
	@ConditionalOnMissingBean(DubboArgumentRewriter.class)
	public DubboArgumentRewriter dubboArgumentRewriter(VariableRender variableRender,
													   DelegateVariableLoader delegateVariableProcessor) {
		return new DefaultDubboArgumentRewriter(variableRender, delegateVariableProcessor);
	}

	@Bean
	@ConditionalOnProperty(prefix = DubboRoutingProperties.PREFIX, name = "rewrite-render", matchIfMissing = true)
	@ConditionalOnMissingBean(VariableRender.class)
	public VariableRender spelRewriterRender() {
		return new SpelVariableRender();
	}

	@Bean
	@ConditionalOnProperty(prefix = DubboRoutingProperties.PREFIX, name = "rewrite-render", havingValue = "velocity")
	@ConditionalOnMissingBean(VariableRender.class)
	public VariableRender velocityRewriterRender() {
		return new VelocityVariableRender();
	}
}
